home *** CD-ROM | disk | FTP | other *** search
- #include <stdlib.h>
- #include <stdio.h>
- #include <conio.h>
- #include <ctype.h>
- #include <time.h>
- #include <dos.h>
- #include <mem.h>
-
- #include "random.h"
- #include "boolean.h"
- #include "fixed.h"
- #include "vga.h"
- #include "mouse.h"
- #include "world.h"
-
- //#define DEBUG
-
- #ifdef DEBUG
-
- static void _Assert
- (
- const char * file,
- unsigned line,
- const char * assertion
- )
- {
- vga.end ();
- fflush (stdout);
- fprintf ( stderr, "\nAssertion failed (%s, line %u): %s\n",
- file, line, assertion );
- fflush (stderr);
- abort ();
- }
-
- #define ASSERT(f) if (f) {} else _Assert ( __FILE__, __LINE__, #f )
-
- #else
-
- #define ASSERT(f)
-
- #endif
-
- //----------------------------------------------------------------------------
-
- const view_size = 64;
- const view_start = view_size / 2;
-
- const win_cen_x = 160;
- const win_cen_y = 100;
-
- const black = 0;
- const cursor_color = 255;
-
- enum key_t
- {
- esc_key = 27,
- home_key = 71,
- up_arrow_key = 72,
- pgup_key = 73,
- lt_arrow_key = 75,
- center_key = 76,
- rt_arrow_key = 77,
- end_key = 79,
- dn_arrow_key = 80,
- pgdn_key = 81
- };
-
- //----------------------------------------------------------------------------
- // The viewer's position variables.
- //----------------------------------------------------------------------------
-
- struct viewer_t
- {
- int x;
- int y;
- int z;
-
- angle_t angle;
- fixed sine;
- fixed cosine;
- };
-
- static viewer_t viewer;
-
- //----------------------------------------------------------------------------
- // The view window.
- //----------------------------------------------------------------------------
-
- static win_t win =
- {
- 0, 0, 320, 200
- };
-
- //----------------------------------------------------------------------------
- // The column data structures used by draw_row.
- //----------------------------------------------------------------------------
-
- struct col_t
- {
- int y;
- unsigned char color;
- };
-
- static col_t col [ 321 ];
- static col_t old_col [ 321 ];
-
- //----------------------------------------------------------------------------
- // FUNCTION mul_and_div
- //----------------------------------------------------------------------------
- // This calculates (a * b) / c with increased precision by using all 64 bits
- // from the multiplication in calculating the result of the division.
- //----------------------------------------------------------------------------
-
- int mul_and_div ( int a, int b, int c );
-
- #pragma aux mul_and_div = \
- "imul ebx" \
- "idiv ecx" \
- parm caller [eax] [ebx] [ecx] \
- value [eax] \
- modify [eax ebx ecx edx];
-
- //----------------------------------------------------------------------------
- // FUNCTION dot_product
- //----------------------------------------------------------------------------
- //
- // dot_product = (u1 * v1 + u2 * v2) / 2^16;
- //
- // This is designed to be used with integer sine and cosine values in the
- // range -2^16..2^16; hence the division by 2^16 following the dot product.
- // Using this function improves precision since it has a 64-bit addition.
- //
- //----------------------------------------------------------------------------
-
- int dot_product ( int u1, int u2, int v1, int v2 );
-
- #pragma aux dot_product = \
- "imul edx" \
- "mov esi, edx" \
- "mov edi, eax" \
- "mov eax, ebx" \
- "imul ecx" \
- "add eax, edi" \
- "adc edx, esi" \
- "shrd eax, edx, 16" \
- parm caller [eax] [ebx] [edx] [ecx] \
- value [eax] \
- modify [eax ebx ecx edx esi edi];
-
- //----------------------------------------------------------------------------
- // FUNCTION view_overhead_map
- //----------------------------------------------------------------------------
-
- static void view_overhead_map
- (
- void
- )
- {
- int start_x = (int) (viewer.x / scale_area) - win_cen_x;
- int start_y = (int) (viewer.y / scale_area) - win_cen_y;
-
- start_x &= clip_mask_x;
- start_y &= clip_mask_y;
-
- // Draw the map, with the viewer's position in the center.
-
- const sequ_addr = 0x3C4;
- const bytes_per_row = 80;
- extern char * a_page;
-
- for ( int plane = 0; plane < 4; ++ plane )
- {
- outpw ( sequ_addr, 0x02 | (0x100 << plane) );
-
- int map_x = start_x;
-
- for ( int x = 0; x < bytes_per_row; ++ x )
- {
- int map_y = start_y;
-
- char * pixel_p = a_page + x;
- char * stop_p = pixel_p + bytes_per_row * 200;
-
- for ( ; pixel_p < stop_p; pixel_p += bytes_per_row )
- {
- *pixel_p = color_map [map_x][map_y];
- map_y = (map_y + 1) & clip_mask_y;
- }
- map_x = (map_x + 4) & clip_mask_x;
- }
- start_x = (start_x + 1) & clip_mask_x;
- }
-
- // Draw a line indicating the viewer's heading.
-
- int x1 = dot_product ( viewer.sine, viewer.cosine, 20, 0 ) + 160;
- int y1 = dot_product ( -viewer.cosine, viewer.sine, 20, 0 ) + 100;
- int x2 = dot_product ( viewer.sine, viewer.cosine, 30, 0 ) + 160;
- int y2 = dot_product ( -viewer.cosine, viewer.sine, 30, 0 ) + 100;
-
- win.line ( x1, y1, x2, y2, cursor_color );
-
- // Draw a marker at the center indicating the viewer's position.
-
- win.rect ( 159, 99, 162, 102, black );
- win.point ( 160, 100, cursor_color );
- }
-
- //----------------------------------------------------------------------------
- //
- // The graphics workhorse routines.
- //
- //----------------------------------------------------------------------------
-
- //----------------------------------------------------------------------------
- // FUNCTION calc_color
- //----------------------------------------------------------------------------
-
- inline unsigned char calc_color
- (
- int map_x,
- int map_y
- )
- {
- return color_map [map_x][map_y];
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION draw_row
- //----------------------------------------------------------------------------
-
- static void draw_row
- (
- void
- )
- {
- const sequ_addr = 0x3C4;
- const bytes_per_row = 80;
- extern char * a_page;
-
- int x;
-
- for ( x = 0; x < 320; ++ x )
- {
- if ( col [x].y < 0 ) col [x].y = 0;
- else if ( col [x].y > 200 ) col [x].y = 200;
- }
-
- for ( int plane = 0; plane < 4; ++ plane )
- {
- outpw ( sequ_addr, 0x02 | (0x100 << plane) );
-
- int col_x = plane;
-
- for ( x = 0; x < bytes_per_row; ++ x )
- {
- ASSERT ( old_col [col_x].y >= 0 );
- ASSERT ( old_col [col_x].y <= 200 );
- ASSERT ( col [col_x].y >= 0 );
- ASSERT ( col [col_x].y <= 200 );
-
- char * pixel_p = & a_page [bytes_per_row*old_col [col_x].y + x];
- char * stop_p = & a_page [bytes_per_row* col [col_x].y + x];
-
- int color = old_col [col_x].color;
-
- int den = col [col_x].y - old_col [col_x].y;
- int num = col [col_x].color - color;
-
- int color_inc;
-
- if ( num < 0 ) { color_inc = -1; num = -num; }
- else color_inc = 1;
-
- int err = 0;
-
- while ( pixel_p < stop_p )
- {
- *pixel_p = (unsigned char) color;
- pixel_p += bytes_per_row;
-
- err += num;
- while ( err >= den )
- {
- err -= den;
- color += color_inc;
- }
- }
-
- old_col [col_x].y = col [col_x].y;
- old_col [col_x].color = col [col_x].color;
-
- col_x += 4;
- }
- }
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION rotate
- //----------------------------------------------------------------------------
- // Takes an (x, y) coordinate pair and rotates it using the variables
- // sine and cosine, which are 32-bit integer values ranging from -65536 to
- // 65536.
- //----------------------------------------------------------------------------
-
- inline void rotate
- (
- int & x,
- int & y,
- int sine,
- int cosine
- )
- {
- int temp_x = x;
- int temp_y = y;
-
- x = dot_product ( cosine, -sine, temp_x, temp_y );
- y = dot_product ( sine, cosine, temp_x, temp_y );
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION view_3d
- //----------------------------------------------------------------------------
-
- static void view_3d
- (
- void
- )
- {
- const sz = 240; // Dist. from viewer's eye to screen in pixels
-
- // Map coordinate system setup:
-
- int viewer_map_x = viewer.x / scale_area;
- int viewer_map_y = viewer.y / scale_area;
-
- int map_start_x, map_start_y;
- int map_start_x_inc, map_start_y_inc;
- int map_x_inc, map_y_inc;
-
- int start_x, start_y;
-
- if ( viewer.angle < 450 || viewer.angle >= 3150 )
- {
- map_start_x = (viewer_map_x - view_start) & clip_mask_x;
- map_start_y = (viewer_map_y - view_start) & clip_mask_y;
- map_start_x_inc = 0;
- map_start_y_inc = 1;
- map_x_inc = 1;
- map_y_inc = 0;
-
- start_x = -view_start * scale_area;
- start_y = view_start * scale_area;
- }
- else if ( viewer.angle < 1350 )
- {
- map_start_x = (viewer_map_x + view_start) & clip_mask_x;
- map_start_y = (viewer_map_y - view_start) & clip_mask_y;
- map_start_x_inc = -1;
- map_start_y_inc = 0;
- map_x_inc = 0;
- map_y_inc = 1;
-
- start_x = view_start * scale_area;
- start_y = view_start * scale_area;
- }
- else if ( viewer.angle < 2250 )
- {
- map_start_x = (viewer_map_x + view_start) & clip_mask_x;
- map_start_y = (viewer_map_y + view_start) & clip_mask_y;
- map_start_x_inc = 0;
- map_start_y_inc = -1;
- map_x_inc = -1;
- map_y_inc = 0;
-
- start_x = view_start * scale_area;
- start_y = -view_start * scale_area;
- }
- else
- {
- map_start_x = (viewer_map_x - view_start) & clip_mask_x;
- map_start_y = (viewer_map_y + view_start) & clip_mask_y;
- map_start_x_inc = 1;
- map_start_y_inc = 0;
- map_x_inc = 0;
- map_y_inc = -1;
-
- start_x = -view_start * scale_area;
- start_y = -view_start * scale_area;
- }
-
- // World coordinate system setup:
-
- start_x -= (viewer.x % scale_area);
- start_y += (viewer.y % scale_area);
-
- int world_x_inc = map_x_inc * scale_area;
- int world_y_inc = -map_y_inc * scale_area;
-
- rotate ( start_x, start_y, viewer.sine, viewer.cosine );
- rotate ( world_x_inc, world_y_inc, viewer.sine, viewer.cosine );
-
- int start_x_inc = world_y_inc;
- int start_y_inc = -world_x_inc;
-
- // Draw rows:
-
- vga.clear ( 0 );
-
- for ( int x = 0; x < 320; ++ x )
- {
- old_col [x].y = 200;
- old_col [x].color = 0;
- col [x].y = 200;
- col [x].color = 0;
- }
-
- for ( int nr_rows = view_size / 2; nr_rows > 0; -- nr_rows )
- {
- int map_x = map_start_x;
- int map_y = map_start_y;
-
- int world_x = start_x;
- int world_y = start_y;
- int world_z = viewer.z - alt_map [map_x][map_y] * scale_height;
-
- int last_sx = 320;
- int last_sy = 0;
- int last_color = 0;
-
- int nr_cols = view_size;
-
- while ( nr_cols > 0 )
- {
- // Calculate the screen coordinates.
-
- if ( world_y > 0 )
- {
- int sx = mul_and_div ( world_x, sz, world_y ) + win_cen_x;
- int sy = mul_and_div ( world_z, sz, world_y ) + win_cen_y;
- int color;
-
- color = calc_color ( map_x, map_y );
-
- // Draw if the point is onscreen.
-
- if ( sx > win.x1 )
- {
- int den = sx - last_sx;
-
- int num = color - last_color;
- int color_inc;
- if ( num < 0 ) { color_inc = -1; num = -num; }
- else color_inc = 1;
- int err = 0;
-
- int num2 = sy - last_sy;
- int last_sy_inc;
- if ( num2 < 0 ) { last_sy_inc = -1; num2 = -num2; }
- else last_sy_inc = 1;
- int err2 = 0;
-
- // Left edge clipping:
-
- if ( last_sx < win.x1 )
- {
- int delta = win.x1 - last_sx;
-
- err = num * delta % den;
- last_color += color_inc * num * delta / den;
-
- err2 = num2 * delta % den;
- last_sy += last_sy_inc * num2 * delta / den;
-
- last_sx = win.x1;
- }
-
- // Right edge clipping:
-
- if ( sx > win.x2 )
- sx = win.x2;
-
- // Draw the section:
-
- for ( int x = last_sx; x < sx; ++ x )
- {
- ASSERT ( x >= 0 && x < 320 );
-
- col [x].y = last_sy;
- col [x].color = (unsigned char) last_color;
-
- err += num;
- while ( err >= den )
- {
- err -= den;
- last_color += color_inc;
- }
-
- err2 += num2;
- while ( err2 >= den )
- {
- err2 -= den;
- last_sy += last_sy_inc;
- }
- }
-
- if ( sx == win.x2 )
- break;
- }
-
- last_sx = sx;
- last_sy = sy;
- last_color = color;
- }
-
- // Go to the next point.
-
- map_x = (map_x + map_x_inc) & clip_mask_x;
- map_y = (map_y + map_y_inc) & clip_mask_y;
-
- world_x += world_x_inc;
- world_y += world_y_inc;
- world_z = viewer.z - alt_map [map_x][map_y] * scale_height;
-
- -- nr_cols;
- }
-
- // Draw this row of points.
-
- draw_row ();
-
- // Move the starting position to the next row.
-
- start_x += start_x_inc;
- start_y += start_y_inc;
-
- map_start_x = (map_start_x + map_start_x_inc) & clip_mask_x;
- map_start_y = (map_start_y + map_start_y_inc) & clip_mask_y;
- }
-
- // Finish the drawing by drawing down to the bottom edge of the screen.
-
- for ( x = 0; x < 320; ++ x )
- {
- col [x].y = win.y2;
- col [x].color = calc_color (viewer_map_x, viewer_map_y);
- }
-
- draw_row ();
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION set_palette
- //----------------------------------------------------------------------------
-
- struct color_t
- {
- char r;
- char g;
- char b;
- };
-
- static void set_palette
- (
- void
- )
- {
- const DAC_write_index = 0x3C8;
- const DAC_data_index = 0x3C9;
-
- color_t colors [ 256 ];
- int i;
-
- memset ( colors, 0, sizeof (colors) );
-
- for ( i = 0; i < 64; ++ i )
- {
- colors [i+1].r = (unsigned char) i;
- colors [i+1].g = (unsigned char) (i * i / 63);
- colors [i+1].b = (unsigned char) (i * i / 63);
- }
-
- colors [255].r = 16;
- colors [255].g = 63;
- colors [255].b = 0;
-
- for ( i = 0; i < 256; ++ i )
- {
- outp ( DAC_write_index, i );
- outp ( DAC_data_index, colors [i].r );
- outp ( DAC_data_index, colors [i].g );
- outp ( DAC_data_index, colors [i].b );
- }
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION interact
- //----------------------------------------------------------------------------
-
- enum view_t
- {
- cockpit_view,
- map_view
- };
-
- static void interact
- (
- void
- )
- {
- const clearance = 8 * scale_height;
- const max_altitude = (max_alt+4) * scale_height;
-
- // Initialize the palette.
-
- set_palette ();
-
- // Store the mouse coordinates so we can tell later if it has moved.
-
- mouse.update ();
- int old_mouse_x = mouse.x;
- int old_mouse_y = mouse.y;
-
- // Initialize the player's position, heading and velocities.
-
- viewer.x = 4 * scale_area;
- viewer.y = 4 * scale_area;
- viewer.z = color_map [4][4] * scale_height + clearance;
- viewer.angle = 1800;
-
- int vel = 0; // Forward velocity
- int vz = 0; // Vertical velocity
- int va = 0; // Angular velocity
-
- // Initialize other variables.
-
- view_t view = cockpit_view;
-
- boolean showing_mouse = false;
- boolean buttons_used = false;
- boolean done = false;
-
- //-------------------------------------------------------------------------
- // The main interaction loop:
- //-------------------------------------------------------------------------
-
- while ( ! done )
- {
- //----------------------------------------------------------------------
- // Update the player's state.
- //----------------------------------------------------------------------
-
- // Heading
-
- viewer.angle += va;
-
- while ( viewer.angle < 0 ) viewer.angle += 3600;
- while ( viewer.angle >= 3600 ) viewer.angle -= 3600;
-
- FIX_cos_sin ( viewer.angle, & viewer.cosine, & viewer.sine );
-
- // Position
-
- int vx = mul_and_div ( vel, -viewer.sine, 65536 );
- int vy = mul_and_div ( vel, viewer.cosine, 65536 );
-
- int map_x = (int) (viewer.x / scale_area);
- int map_y = (int) (viewer.y / scale_area);
-
- viewer.x += vx;
- viewer.y += vy;
-
- while ( viewer.x < 0 ) viewer.x += world_size_x;
- while ( viewer.x >= world_size_x ) viewer.x -= world_size_x;
-
- while ( viewer.y < 0 ) viewer.y += world_size_y;
- while ( viewer.y >= world_size_y ) viewer.y -= world_size_y;
-
- // Elevation
-
- viewer.z += vz;
-
- if ( viewer.z < alt_map [map_x][map_y] * scale_height + clearance )
- {
- viewer.z = alt_map [map_x][map_y] * scale_height + clearance;
- vz = 0;
- }
- else if ( viewer.z > max_altitude )
- {
- viewer.z = max_altitude;
- vz = 0;
- }
-
- //----------------------------------------------------------------------
- // Draw the view.
- //----------------------------------------------------------------------
-
- if ( view == cockpit_view ) view_3d ();
- else view_overhead_map ();
-
- if ( showing_mouse )
- {
- win.rect ( 0, mouse.y-2, 3, mouse.y+3, black );
- win.rect ( 0, mouse.y-1, 2, mouse.y+2, cursor_color);
- win.rect ( 317, mouse.y-2, 320, mouse.y+3, black );
- win.rect ( 318, mouse.y-1, 320, mouse.y+2, cursor_color);
- win.rect ( mouse.x-2, 0, mouse.x+3, 3, black );
- win.rect ( mouse.x-1, 0, mouse.x+2, 2, cursor_color);
- }
-
- vga.update ();
-
- //----------------------------------------------------------------------
- // Handle keyboard input.
- //----------------------------------------------------------------------
-
- while ( kbhit () )
- {
- int key = tolower (getch ());
-
- if ( key == 0 )
- {
- key = getch ();
-
- switch ( key )
- {
- case up_arrow_key : vz += scale_height / 2; break;
- case dn_arrow_key : vz -= scale_height / 2; break;
- case lt_arrow_key : va -= 5; break;
- case rt_arrow_key : va += 5; break;
- case center_key : vel = 0; va = 0; vz = 0; break;
- default: break;
- }
- }
- else switch ( key )
- {
- case esc_key: // Drop through
- case 'q': done = true; break;
- case 'm':
- view = (view == cockpit_view) ? map_view : cockpit_view;
- break;
- case 'n': viewer.angle = 0; break;
- case 'e': viewer.angle = 900; break;
- case 's': viewer.angle = 1800; break;
- case 'w': viewer.angle = 2700; break;
- case 'r': viewer.angle = (viewer.angle + 1800) % 3600; break;
- case '+': // Drop through
- case '=': vel -= scale_area / 8; break;
- case '-': vel += scale_area / 8; break;
- default: break;
- }
-
- showing_mouse = false;
- }
-
- //----------------------------------------------------------------------
- // Handle mouse input.
- //----------------------------------------------------------------------
-
- if ( mouse.exists )
- {
- mouse.update ();
-
- if ( mouse.x != old_mouse_x || mouse.y != old_mouse_y )
- {
- showing_mouse = true;
-
- old_mouse_x = mouse.x;
- old_mouse_y = mouse.y;
-
- va = mouse.x - 160;
- vz = mouse.y - 100;
-
- // Give the mouse an exponential response curve.
-
- if (va < 0) va = va * va / -160;
- else va = va * va / 160;
-
- if (vz < 0) vz = vz * vz / (scale_height / 16);
- else vz = vz * vz / -(scale_height / 16);
- }
-
- if ( mouse.buttons ) // If any button is down
- {
- if ( ! buttons_used )
- {
- view = (view == cockpit_view) ? map_view : cockpit_view;
-
- buttons_used = true;
- }
- }
- else
- {
- buttons_used = false;
- }
- }
- }
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION check_equipment
- //----------------------------------------------------------------------------
- // Check for a mouse.
- //----------------------------------------------------------------------------
-
- static void check_equipment ( void )
- {
- mouse.init ();
-
- if ( mouse.exists )
- {
- printf ( "Mouse detected.\n" );
- }
- else
- {
- printf ( "No mouse detected.\n" );
- }
-
- if ( ! vga.exists () )
- {
- fprintf ( stderr, "This program requires a VGA graphics adaptor.\n" );
- exit (1);
- }
- }
-
- //----------------------------------------------------------------------------
- // FUNCTION main
- //----------------------------------------------------------------------------
- // Sets up and takes down the game environment. Checks first to be sure
- // the necessary equipment is available, then changes screen modes before
- // passing control to the interactive game-playing module. Upon regaining
- // control, main resets the screen to its previous state.
- //----------------------------------------------------------------------------
-
- const char * instructions =
-
- "\n\"Moonbase\" 3D Landscape Demo\n"
- "────────────────────────────\n\n"
- "Use the mouse or arrow keys to steer. Other keys are:\n\n"
- " + - Increase/Decrease forward velocity\n"
- " m Toggle satellite view\n"
- " Esc Quit\n"
- "\nPress Enter to begin.\n"
- "";
-
- const char * closing_msg =
-
- "Moonbase was written by James McNeill (mcneja@wwc.edu)\n"
- "using Watcom C++ and the PMODE/W DOS extender.\n";
-
- int main
- (
- void
- )
- {
- check_equipment ();
-
- WORLD_generate ();
-
- printf ( instructions );
- if ( getch () == 0 ) getch ();
-
- vga.start ();
- interact ();
- vga.end ();
-
- printf ( closing_msg );
-
- return 0;
- }
-